{
    "cells": [
        {
            "cell_type": "raw",
            "metadata": {},
            "source": [
                "---\n",
                "title: Python types\n",
                "sidebar_position: 2\n",
                "description: Using Mojo types in Python, and Python types in Mojo.\n",
                "css: /static/styles/page-navigation.css\n",
                "website:\n",
                "  open-graph:\n",
                "    image: /static/images/mojo-social-card.png\n",
                "  twitter-card:\n",
                "    image: /static/images/mojo-social-card.png\n",
                "---\n"
            ]
        },
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                "When calling Python methods, Mojo needs to convert back and forth between native\n",
                "Python objects and native Mojo objects. Most of these conversions happen\n",
                "automatically, but there are a number of cases that Mojo doesn't handle yet.\n",
                "In these cases you may need to do an explicit conversion, or call an extra\n",
                "method.\n",
                "\n",
                "## Mojo types in Python\n",
                "\n",
                "Mojo primitive types implicitly convert into Python objects.\n",
                "Today we support lists, tuples, integers, floats, booleans, and strings.\n",
                "\n",
                "For example, given this Python function that prints Python types:"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 2,
            "metadata": {},
            "outputs": [],
            "source": [
                "%%python\n",
                "def type_printer(value):\n",
                "    print(type(value))"
            ]
        },
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                "(You can ignore the `%%python` at the start of the code sample; it's explained\n",
                "in the note below.)\n",
                "\n",
                "You can pass this Python function Mojo types with no problem:"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 3,
            "metadata": {},
            "outputs": [
                {
                    "name": "stdout",
                    "output_type": "stream",
                    "text": [
                        "<class 'int'>\n",
                        "<class 'float'>\n",
                        "<class 'tuple'>\n"
                    ]
                }
            ],
            "source": [
                "type_printer(4)\n",
                "type_printer(3.14)\n",
                "type_printer((\"Mojo\", True))"
            ]
        },
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                ":::note\n",
                "\n",
                "This is a simplified code example written as a set of Jupyter\n",
                "notebook cells. The first cell includes the `%%python` directive so it's\n",
                "interpreted as Python. The second cell includes top-level Mojo code. You'd need\n",
                "to adjust this code to run it elsewhere.\n",
                "\n",
                ":::"
            ]
        },
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                "## Python types in Mojo\n",
                "\n",
                "You can also use Python objects from Mojo. For example, Mojo doesn't have a\n",
                "standard dictionary type yet, but you can work with Python dictionaries in Mojo. \n",
                "To create a Python dictionary, use the \n",
                "[`dict()`](/mojo/stdlib/python/python.html#dict) method:"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 4,
            "metadata": {},
            "outputs": [],
            "source": [
                "from python import Python\n",
                "\n",
                "fn use_dict() raises:\n",
                "    var dictionary = Python.dict()\n",
                "    dictionary[\"fruit\"] = \"apple\"\n",
                "    dictionary[\"starch\"] = \"potato\"\n",
                "    print(\"Fruit: \", dictionary[\"fruit\"])"
            ]
        },
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                "### Mojo wrapper objects\n",
                "\n",
                "When you use Python objects in your Mojo code, Mojo adds the \n",
                "[`PythonObject`](/mojo/stdlib/python/object.html#pythonobject) wrapper around\n",
                "the Python object. This object exposes a number of common double underscore\n",
                "methods (dunder methods) like `__getitem__()` and `__getattr__()`, passing them\n",
                "through to the underlying Python object. "
            ]
        },
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                "You can explicitly create a wrapped Python object by initializing a \n",
                "`PythonObject` with a Mojo literal:"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 5,
            "metadata": {},
            "outputs": [],
            "source": [
                "from python.object import PythonObject\n",
                "\n",
                "var py_list: PythonObject = [1, 2, 3, 4]"
            ]
        },
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                "Most of the time, you can treat the wrapped object just like you'd treat it in \n",
                "Python. You can use Python's `[]` operators to access an item in a list, and use\n",
                "dot-notation to access attributes and call methods. For example:"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 6,
            "metadata": {},
            "outputs": [],
            "source": [
                "var n = py_list[2]\n",
                "py_list.append(5)"
            ]
        },
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                "\n",
                "If you want to construct a Python type that doesn't have a literal Mojo \n",
                "equivalent, you can also use the \n",
                "[`Python.evaluate()`](/mojo/stdlib/python/python.html#evaluate) method. For\n",
                "example, to create a Python `set`:"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 20,
            "metadata": {},
            "outputs": [],
            "source": [
                "fn use_py_set() raises:\n",
                "    var py_set = Python.evaluate('set([2, 3, 5, 7, 11])')\n",
                "    var num_items = len(py_set)\n",
                "    print(num_items, \" items in set.\")  # prints \"5 items in set\"\n",
                "    print(py_set.__contains__(6))       # prints \"False\""
            ]
        },
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                "TODO: You should be able to use the expression `6 in py_set`. However, because\n",
                "of the way `PythonObject` currently works, you need to call the \n",
                "`__contains__()` method directly.\n",
                "\n",
                "Some Mojo APIs handle `PythonObject` just fine, but sometimes you'll need to \n",
                "explicitly convert a Python value into a native Mojo value. \n",
                "\n",
                "Currently `PythonObject` conforms to the \n",
                "[`Intable`](/mojo/stdlib/builtin/int.html#intable) and \n",
                "[`Stringable`](/mojo/stdlib/builtin/str.html#stringable) traits, which means you\n",
                "can  convert Python values to Mojo `Int` and `String` types using the built-in \n",
                "[`int()`](/mojo/stdlib/builtin/int.html#int-1) and\n",
                "[`str()`](/mojo/stdlib/builtin/str.html#str) functions, and print Python values\n",
                "using the built-in [`print()`](/mojo/stdlib/builtin/io.html#print) function.\n",
                "  \n",
                "`PythonObject` also provides the\n",
                "[`__bool__()`](/mojo/stdlib/python/object.html#bool__) and \n",
                "[`to_float64()`](/mojo/stdlib/python/object.html#to_float64) methods for \n",
                "converting to boolean and floating point values, respectively.\n",
                "\n",
                "```mojo\n",
                "var i: Int = int(py_int)\n",
                "var s: String = str(py_string)\n",
                "var b: Bool = py_bool.__bool__()\n",
                "var f: Float64 = py_float.to_float64()\n",
                "```\n",
                "\n",
                ":::note\n",
                "\n",
                "We mentioned that Python types get wrapped in `PythonObject` wrapper. \n",
                "There is currently one exception to this: Python dictionaries have their own\n",
                "specialized Mojo wrapper type, `Dictionary`. Despite the name, it's not a true\n",
                "dictionary type, just another kind of wrapper. Most of the time this is\n",
                "just an implementation detail, but you may notice that the types are different.\n",
                "\n",
                ":::\n",
                "\n",
                "### Comparing Python types in Mojo\n",
                "\n",
                "In conditionals, Python objects act like you'd expect them to: Python values \n",
                "like `False` and `None` evaluate as false in Mojo, too.\n",
                "\n",
                "If you need to know the type of the underlying Python object, you can use the \n",
                "[`Python.type()`](/mojo/stdlib/python/python.html#type) method, which is \n",
                "equivalent to the Python `type()` builtin. You can compare the identity of two\n",
                "Python objects using the\n",
                "[`Python.is_type()`](/mojo/stdlib/python/python.html#is_type) method (which is\n",
                "equivalent to the Python `is` operator):"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 24,
            "metadata": {},
            "outputs": [],
            "source": [
                "fn python_types() raises:\n",
                "    from python import Python\n",
                "    from python.object import PythonObject\n",
                "\n",
                "    var value1: PythonObject = 3.7\n",
                "    var value2 = Python.evaluate(\"10/3\")\n",
                "    var float_type = Python.evaluate(\"float\")\n",
                "\n",
                "    print(Python.type(value1))   # <class 'float'>\n",
                "    print(Python.is_type(Python.type(value1), Python.type(value2)))  # True\n",
                "    print(Python.is_type(Python.type(value1), float_type))           # True\n",
                "    print(Python.is_type(Python.type(value1), Python.none()))        # False"
            ]
        },
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                "One TODO item here: The `Python.is_type()` method is misleadingly named, since \n",
                "it doesn't compare _types_, but object identity.\n",
                "\n",
                "## Further reading\n",
                "\n",
                "For more information, see \n",
                "[Using Mojo with Python](https://www.modular.com/blog/using-mojo-with-python) on \n",
                "the Modular Blog."
            ]
        }
    ],
    "metadata": {
        "kernelspec": {
            "display_name": "Mojo",
            "language": "mojo",
            "name": "mojo-jupyter-kernel"
        },
        "language_info": {
            "codemirror_mode": {
                "name": "mojo"
            },
            "file_extension": ".mojo",
            "mimetype": "text/x-mojo",
            "name": "mojo"
        },
        "orig_nbformat": 4
    },
    "nbformat": 4,
    "nbformat_minor": 2
}
